/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.epam.expertsys;

import java.util.regex.*;
import java.io.*;

/**
 *
 * @author Admin
 */
public class JsonParse {

    /**
     * Returns the number of regex-pattern group that is currently hit
     * If several groups are hit, returns group with minimum index
     * If no groups are hit, returns -1
     * @param m
     * @return
     */
    private static int groupHit(Matcher m) {
        for (int count = 1; count <= m.groupCount(); count++) {
            if (m.group(count) != null) {
                return count;
            }
        }
        return -1;
    }

    /**
     * Attempts to parse exactly one fact from inStream
     * 
     * @param inStream
     * @return
     * @throws IOException
     * @throws RecNotClassCompatibleException
     * @throws NotJsonFormatException
     * @throws NullValueException
     */
    public static Fact factParse(BufferedReader inStream) throws IOException,
            RecNotClassCompatibleException, NotJsonFormatException,
            NullValueException {
        //TODO: Replace RecNotClassCompatibleException with standart DataFormatException


        // Amount of opened braces
        int figBraces = 0;
        //Amount of opened brackets
        int squareBraces = 0;
        //True if parser has reached the end of record
        boolean isRecordEnd = false;
        String vName = null;
        String vVal = null;

        Pattern jsonPat = Pattern.compile("(\\{)|(?:\"(\\w+)\")|(\\})|"
                + "(,)|(\\[)|(\\])|(:)|(\\s+)");
        /*
         * Group indeces:
         * 1 - {
         * 2 - varname or varval (without parenthesis)
         * 3 - }
         * 4 - comma
         * 5 - [
         * 6 - ]
         * 7 - colon
         * 8 - any whitespace sequence
         */

        boolean[] flags = {false, false, false, false, false,
            false, false, false, true, false, false};
        /*
         * Flag meanings:
         * 0 - comma expected
         * 1 - varName expected
         * 2 - varValue expected
         * 3 - varName initialized
         * 4 - varValue initialized
         * 5 - colon was here
         * 6 - colon expected
         * 7 - string expected
         * 8 - opening braces expected
         * 9 - closing braces expected
         * 10 - object opened
         */

        String curString = inStream.readLine();

        while ((curString != null) && !isRecordEnd) {
            Matcher jMatch = jsonPat.matcher(curString);
            while (jMatch.find()) {
                int curHit = JsonParse.groupHit(jMatch);
                switch (curHit) {
                    case 1:
                        if (!flags[8]) {
                            throw new NotJsonFormatException("'{' not expected");
                        }
                        figBraces++;
                        if (figBraces > 1) {
                            throw new RecNotClassCompatibleException();
                        }
                        flags[7] = true; // string expected
                        flags[8] = false; // opening braces not expected
                        flags[10] = true; // object opened
                        break;

                    case 2:
                        if (!flags[7] || !flags[10]) {
                            throw new NotJsonFormatException(
                                    "String not expected");
                        }
                        if (flags[1] && flags[5]) { //waiting for vName
                            vName = jMatch.group(curHit);
                            flags[3] = true; // vName is initialised
                            flags[1] = false; // vName not expected anymore
                            flags[0] = true; // comma expected
                            flags[5] = false; //last match is not colon
                            flags[9] = true;
                        } else if (flags[2] && flags[5]) { // waiting for vVal
                            vVal = jMatch.group(curHit);
                            flags[4] = true; // vVal is isnitialised
                            flags[2] = false;// vVal not expected anymore
                            flags[0] = true;// comma expected
                            flags[5] = false; //last match is not colon
                            flags[9] = true;
                        } else if ("varName".equals(jMatch.group(curHit))
                                && !flags[3]) {
                            flags[1] = true; // vName expected
                            flags[6] = true; // colon expected
                        } else if ("varValue".equals(jMatch.group(curHit))
                                && !flags[4]) {
                            flags[2] = true; // vVal expected
                            flags[6] = true; // colon expected
                        } else {
                            throw new RecNotClassCompatibleException();
                        }
                        flags[7] = false; // string not expected

                        break;

                    case 3:
                        if (!flags[9] || !flags[10] || figBraces == 0) {
                            throw new NotJsonFormatException("'}' not expected");
                        }
                        figBraces--;
                        /*
                         * if figBraces>0 flags[0] = true - implied
                         */
                        if (figBraces == 0) {
                            isRecordEnd = true;
                        }
                        //flags[9] = false;
                        break;

                    case 4:
                        if (!flags[0] || !flags[10]) {
                            throw new NotJsonFormatException("',' not expected");
                        }
                        flags[0] = false;
                        flags[7] = true;
                        //aaaaaa
                        break;

                    case 5:
                        throw new RecNotClassCompatibleException("No arrays");


                    case 6:
                        throw new RecNotClassCompatibleException("No arrays");


                    case 7:
                        if (!flags[6] || !flags[10]) {
                            throw new NotJsonFormatException("':' not expected");
                        }
                        flags[6] = false; // colon not expected
                        flags[5] = true; // previous symbol is colon
                        flags[7] = true; // string expected
                        break;

                    case 8:
                        //aaaaaa
                        break;

                    default:
                        //aaaaaa
                        break;
                }
            }
            curString = inStream.readLine();
        }

        return Fact.create(vName, vVal);

    }
}
